#! /usr/bin/env python
"""
::BOH
$Id: OWTFparser.py,v 1.5 2007/01/9 00:00:00

Copyright (c) 2006 George Bobeck. All rights reserved.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at
your option) any later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
::EOH

XML parser for OWTF sensor readings and OWTF configuration files.
"""
from xml.dom import minidom, Node
from datetime import *
#from reading import *
import re, textwrap, md5

class HandleXML:
    def __init__(self):
        pass
    
    def ParseFromString(self, s):
        return minidom.parseString(s)

    def ParseFromFile(self, f):
        return minidom.parse(f)

class ConfScanner:
    def gettext(self, nodelist):
        """
        given a list of one or more nodes, recursively find all text
        nodes in a given list (or children of nodes in that list),
        concatenates them, removes dupe spaces, and returns the result.
        """
        retlist = []
        for node in nodelist:
            if node.nodeType == Node.TEXT_NODE:
                retlist.append(node.wholeText)
            elif node.hasChildNodes:
                retlist.append(self.gettext(node.childNodes))
        return re.sub('\s+', ' ', ''.join(retlist))

    def __init__(self, doc):
        #database settings
        self.collector_db = [None]
        self.alert_db = [None]
        #smtp settings
        self.alert_smtp = [None]
        #generic alert settings
        self.alert_resolution = '600'
        self.alert_verbose = 'False'
        self.monitor_OW = None
        self.monitorID = None
        self.monitor_resolution = '600'
        self.monitor_verbose = 'False'
        self.monitor_destination_address = None
        self.monitor_destination_port = '50000'
        self.collector_host = ""
        self.collector_port = '50000'
        self.collector_verbose = 'False'
        self.sensors = None
        
        for child in doc.childNodes:
            if child.nodeType == Node.ELEMENT_NODE and child.tagName =="configuration":
                self.handleConfig(child)

    def handleConfig(self, node):
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'alert_configuration':
                self.handleAlertConfig(child)
            if child.tagName == 'collector_configuration':
                self.handleCollectorConfig(child)
            if child.tagName == 'sensor_configuration':
                self.sensors = self.handleSensorConfig(child)
            if child.tagName == 'monitor_configuration':
                self.handleMonitorConfig(child)

    def handleAlertConfig(self, node):
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'smtp':
                self.alert_smtp = self.handleSMTPConfig(child)
            if child.tagName == 'database':
                self.alert_db = self.handleDBConfig(child)
            if child.tagName == 'alert_resolution':
                self.alert_resolution = int(self.gettext(child.childNodes))
            if child.tagName == 'verbose_mode':
                v = str(self.gettext(child.childNodes))
                if v == "True":
                    self.alert_verbose = True
                else:
                    self.alert_verbose = False
                
                #self.alert_verbose = str(self.gettext(child.childNodes))

    def handleCollectorConfig(self, node):
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'collector_host':
                self.collector_host = str(self.gettext(child.childNodes))
            if child.tagName == 'collector_port':
                self.collector_port = str(self.gettext(child.childNodes))
            if child.tagName == 'verbose_mode':
                v = str(self.gettext(child.childNodes))
                if v == "True":
                    self.collector_verbose = True
                else:
                    self.collector_verbose = False
                #self.collector_verbose = str(self.gettext(child.childNodes))
                
            if child.tagName == 'database':
                self.collector_db = self.handleDBConfig(child)

    def handleSensorConfig(self, node):
        sensors = []
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'sensor':
                sensors.append(self.handleSensors(child))
        return sensors

    def handleSensors(self, node):
        sensor = [None,None]
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'sid':
                sensor[0] = str(self.gettext(child.childNodes))
            if child.tagName == 'location':
                sensor[1] = str(self.gettext(child.childNodes))
        return sensor

    def handleSMTPConfig(self, node):
        """
        | 0 | self.smtp_server = None
        | 1 | self.smtp_verify = 'False'
        | 2 | self.smtp_destination_address = None
        | 3 | self.smtp_source_address = None
        """
        smtp = [None,None,None,None]
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'smtpserver':
                smtp[0] = str(self.gettext(child.childNodes))
            if child.tagName == 'smtp_verify':
                v = str(self.gettext(child.childNodes))
                if v == "True":
                    smtp[1] = True
                else:
                    smtp[1] = False
            if child.tagName == 'destination_address':
                smtp[2] = str(self.gettext(child.childNodes))
            if child.tagName == 'source_address':
                smtp[3] = str(self.gettext(child.childNodes))
        return smtp

    def handleDBConfig(self, node):
        """
        | mysql | sqlite | value
        | 0     | 0      | db_type = None # for BOTH
        | -     | 1      | db_location = None # for sqlite3
        | 1     | -      | db_host = None # for mysql
        | 2     | -      | db_port = '3306' # for mysql
        | 3     | -      | db_user = None # for mysql
        | 4     | -      | db_password = None # for mysql
        | 5     | 2      | db_name = None # for BOTH
        """
        database = [None,None,None,None,None,None,None]
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'type':
                database[0] = str(self.gettext(child.childNodes))
            if child.tagName == 'location':
                database[1] = str(self.gettext(child.childNodes))
            if child.tagName == 'host':
                database[2] = str(self.gettext(child.childNodes))
            if child.tagName == 'port':
                database[3] = str(self.gettext(child.childNodes))
            if child.tagName == 'user':
                database[4] = str(self.gettext(child.childNodes))
            if child.tagName == 'password':
                database[5] = str(self.gettext(child.childNodes))
            if child.tagName == 'db':
                database[6] = str(self.gettext(child.childNodes))
        if database[0] == "sqlite":
            database = [database[0],database[1],database[6]]
        else:
            database.pop(1)
        return database

    def handleMonitorConfig(self, node):
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'OWsource':
                self.monitor_OW = str(self.gettext(child.childNodes))
            if child.tagName == 'resolution':
                self.monitor_resolution = int(self.gettext(child.childNodes))
            if child.tagName == 'Monitor_ID':
                self.monitorID = str(self.gettext(child.childNodes))
            if child.tagName == 'destination_address':
                self.monitor_destination_address = str(self.gettext(child.childNodes))
            if child.tagName == 'destination_port':
                self.monitor_destination_port = str(self.gettext(child.childNodes))
            if child.tagName == 'verbose_mode':
                v = str(self.gettext(child.childNodes))
                if v == "True":
                    self.monitor_verbose = True
                else:
                    self.monitor_verbose = False
                #self.monitor_verbose = str(self.gettext(child.childNodes))

    """
    Accessor Functions
    """
    def getAlertSMTPConfig(self):
        return self.alert_smtp
    def getAlertDBConfig(self):
        return self.alert_db
    def getCollectorDBConfig(self):
        return self.collector_db
    def getAlertResolution(self):
        """
        Takes self.alert_resolution, returns int version
        """
        return int(self.alert_resolution)
    def getAlertVerbose(self):
        """
        Takes self.alert_verbose, returns boolean version
        """
        v = str(self.alert_verbose)
        if v == "True":
            return True
        else:
            return False
    def getCollectorHost(self):
        return str(self.collector_host)
    def getCollectorPort(self):
        return str(self.collector_port)
    def getCollectorVerbose(self):
        """
        Takes self.collector_verbose, returns boolean version
        """
        #return str(self.collector_verbose)
        v = str(self.collector_verbose)
        if v == "True":
            return True
        else:
            return False
    def getSensors(self):
        return self.sensors
    def getMonitorOWSource(self):
        return self.monitor_OW
    def getMonitorID(self):
        return self.monitorID
    def getMonitorResolution(self):
        return int(self.monitor_resolution)
    def getMonitorVerbose(self):
        """
        Takes self.monitor_verbose, returns boolean version
        """
        #return self.monitor_verbose
        v = str(self.monitor_verbose)
        if v == "True":
            return True
        else:
            return False
    def getMonitorDestinationAddress(self):
        return self.monitor_destination_address
    def getMonitorDestinationPort(self):
        return self.monitor_destination_port

class ReadingScanner:
    def gettext(self, nodelist):
        """
        given a list of one or more nodes, recursively find all text
        nodes in a given list (or children of nodes in that list),
        concatenates them, removes dupe spaces, and returns the result.
        """
        retlist = []
        for node in nodelist:
            if node.nodeType == Node.TEXT_NODE:
                retlist.append(node.wholeText)
            elif node.hasChildNodes:
                retlist.append(self.gettext(node.childNodes))
        return re.sub('\s+', ' ', ''.join(retlist))

    def __init__(self, doc):
        self.monitor_name = None
        self.sensor_data = []    
        for child in doc.childNodes:
            if child.nodeType == Node.ELEMENT_NODE and child.tagName =="reading":
                self.handleReading(child)

    def handleReading(self, node):
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'monitor':
                self.handleMonitor(child)

    def handleMonitor(self, node):
        """
        Generic Monitor handler.
        Returns (ok, sets the value of) monitor name 
        and a list of sensors with sensor data. 
        """
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'monitor-name':
                self.monitor_name = self.gettext(child.childNodes)
            if child.tagName == 'sensor':
                self.sensor_data.append(self.handleSensor(child))
    
    def handleSensor(self, node):
        """
        Generic Sensor handler.
        Returns a list of lists.
        For each list item: 
            position 0 is the Sensor ID
            position 1 is the Sensor Location
            position 2 is the Sensor Data (list)
        """
        sensors=['','',None]
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'sensor-name':
                sensors[0] = str(self.gettext(child.childNodes))
            if child.tagName == 'location':
                sensors[1] = str(self.gettext(child.childNodes))
            if child.tagName == 'values':
                sensors[2]= self.handleValues(child)
        return sensors

    def handleValues(self, node):
        """
        Generic sensor data handling.  
        Returns a list which contains all sensor data.
        Cron strings should always be in position 0.
        """
        sensordata=['',None,None,None,None]
        for child in node.childNodes:
            if child.nodeType != Node.ELEMENT_NODE:
                continue
            if child.tagName == 'cron':
                sensordata[0]= str(self.gettext(child.childNodes))
            if child.tagName == 'temp':
                sensordata[1]= str(self.gettext(child.childNodes))
            if child.tagName == 'humid':
                sensordata[2]= str(self.gettext(child.childNodes))
            if child.tagName == 'V1':
                sensordata[3]= str(self.gettext(child.childNodes))
            if child.tagName == 'V2':
                sensordata[4]= str(self.gettext(child.childNodes))
        return sensordata

    """
    Accessor Functions
    """
    def getMonitorName(self):
        return self.monitor_name

    def getSensorData(self):
        return self.sensor_data

    def toReading(self):
        """
        returns a reading oblect or a list of reading objects.
        """
        t1 = []
        t = Reading()
        if len(self.sensor_data)==1:
            t.SetMID(self.monitor_name)
            t.SetSID( self.sensor_data[0][0])
            t.SetLocation( self.sensor_data[0][1])
            t.SetTime(self.sensor_data[0][2][0])
            t.SetTemp(float(self.sensor_data[0][2][1]))
            t.SetHumid(float(self.sensor_data[0][2][2]))
            t.SetV1(float(self.sensor_data[0][2][3]))
            t.SetV2(float(self.sensor_data[0][2][4]))
            return t
        else:
             for s in self.sensor_data:
                 t.SetMID(self.monitor_name)
                 t.SetSID(s[0])
                 t.SetLocation(s[1])
                 t.SetTime(s[2][0])
                 t.SetTemp(float(s[2][1]))
                 t.SetHumid(float(s[2][2]))
                 t.SetV1(float(s[2][3]))
                 t.SetV2(float(s[2][4]))
                 t1.append(t)
             return t1
"""

"""
class Reading:
    def __init__(self):
        self.mid = "None" # monitor ID
        self.sid = "None" # sensor ID
        self.location = "None" # sensor location 
        self.cron = "0000-01-01 00:00:00" # Datetime Format should be YYYY-MM-DD HH:MM:SS 
        self.temp = 0.0 # temperature, OWFS default
        self.humid = 0.0 # humidity, %
        self.V1 = 0.0 # Voltage 1, extra data slot
        self.V2 = 0.0 # Voltage 2, extra data slot

    def SetMID(self,a):
        self.mid = a
    def GetMID(self):
        return self.mid
    
    def SetSID(self,a):
        self.sid = a
    def GetSID(self):
        return self.sid

    def SetLocation(self,a):
        self.location = a
    def GetLocation(self):
        return self.location

    def SetTime(self,a):
        self.cron = a
    def GetTime(self):
        return self.cron

    def SetCron(self,a):
        """
        ALIAS for SetTime()
        """
        self.cron = a
    def GetCron(self):
        """
        ALIAS for GetTime()
        """
        return self.cron

    def SetTemp(self,a):
        self.temp = float(a)
    def GetTemp(self):
        return self.temp

    def SetHumid(self,a):
        self.humid = float(a)
    def GetHumid(self):
        return self.humid

    def SetV1(self,a):
        self.V1 = float(a)
    def GetV1(self):
        return self.V1

    def SetV2(self,a):
        self.V2 = float(a)
    def GetV2(self):
        return self.V2

    def CtoF(self):
        cel = float( self.temp )
        ft = ( cel * ( 9.0/5.0 ) ) + 32.0
        return ft
    def FtoC(self):
        ft = float( self.temp )
        cel = ((ft - 32.0) * (5.0/9.0))
        return cel
    def CtoK(self):
        kel = self.temp + 273.18
        return kel
    def KtoC(self):
        cel = self.temp - 273.18 
        return cel

    def toXML(self):
        doc = minidom.Document()
        #create <reading>
        reading = doc.createElement('reading')
        doc.appendChild(reading)
        #create <monitor>
        monitor = doc.createElement('monitor')
        reading.appendChild(monitor)
        monitorID = doc.createElement('monitor-name')
        monitorID.appendChild(doc.createTextNode(self.mid))
        monitor.appendChild(monitorID)
        # create <sensor>
        sensor = doc.createElement('sensor')
        monitor.appendChild(sensor)
        sensorID = doc.createElement('sensor-name')
        sensorID.appendChild(doc.createTextNode(self.sid))
        sensor.appendChild(sensorID)
        location = doc.createElement('location')
        location.appendChild(doc.createTextNode(self.location))
        sensor.appendChild(location)
        #create <values>
        values = doc.createElement('values')
        sensor.appendChild(values)
        cron = doc.createElement('cron')
        cron.appendChild(doc.createTextNode(self.cron))
        values.appendChild(cron)
        temp = doc.createElement('temp')
        temp.appendChild(doc.createTextNode(str(self.temp)))
        values.appendChild(temp)
        humid = doc.createElement('humid')
        humid.appendChild(doc.createTextNode(str(self.humid)))
        values.appendChild(humid)
        V1 = doc.createElement('V1')
        V1.appendChild(doc.createTextNode(str(self.V1)))
        values.appendChild(V1)
        V2 = doc.createElement('V2')
        V2.appendChild(doc.createTextNode(str(self.V2)))
        values.appendChild(V2)
        return doc.toxml()

    def toPrettyXML(self,indent = '     '):
        """
        Similar to toXML(), however, this is meant to be
        human readable.  This will add some whitespace  
        (one space)before and after each Text Node.
        """
        doc = minidom.Document()
        #create <reading>
        reading = doc.createElement('reading')
        doc.appendChild(reading)
        #create <monitor>
        monitor = doc.createElement('monitor')
        reading.appendChild(monitor)
        monitorID = doc.createElement('monitor-name')
        monitorID.appendChild(doc.createTextNode(self.mid))
        monitor.appendChild(monitorID)
        # create <sensor>
        sensor = doc.createElement('sensor')
        monitor.appendChild(sensor)
        sensorID = doc.createElement('sensor-name')
        sensorID.appendChild(doc.createTextNode(self.sid))
        sensor.appendChild(sensorID)
        location = doc.createElement('location')
        location.appendChild(doc.createTextNode(self.location))
        sensor.appendChild(location)
        #create <values>
        values = doc.createElement('values')
        sensor.appendChild(values)
        cron = doc.createElement('cron')
        cron.appendChild(doc.createTextNode(self.cron))
        values.appendChild(cron)
        temp = doc.createElement('temp')
        temp.appendChild(doc.createTextNode(str(self.temp)))
        values.appendChild(temp)
        humid = doc.createElement('humid')
        humid.appendChild(doc.createTextNode(str(self.humid)))
        values.appendChild(humid)
        V1 = doc.createElement('V1')
        V1.appendChild(doc.createTextNode(str(self.V1)))
        values.appendChild(V1)
        V2 = doc.createElement('V2')
        V2.appendChild(doc.createTextNode(str(self.V2)))
        values.appendChild(V2)
        return doc.toprettyxml(indent)
"""

"""
# Test Data.  To be removed

#sample = "<configuration><alert_configuration><smtp><smtpserver>my_smtp_server.example.com</smtpserver><smtp_verify>False</smtp_verify><destination_address>my_email@example.com</destination_address><source_address>owtf_alert@owtf_daemon</source_address></smtp><database><type>mysql</type><host>my_mysql_server</host><port>3306</port><user>my_user</user><password>my_password</password><db>owtf</db></database><alert_resolution>600</alert_resolution><verbose_mode>True</verbose_mode></alert_configuration></configuration>"

#sample2 = "<?xml version='1.0' encoding='utf-8'?><reading><monitor><monitor-name>mid</monitor-name><sensor><sensor-name>sid</sensor-name><location>location</location><values><cron>cron1</cron><temp>2</temp><humid>3</humid><V1>4</V1><V2>5</V2></values></sensor><sensor><sensor-name>sid</sensor-name><location>location</location><values><cron>cron2</cron><temp>2</temp><humid>3</humid><V1>4</V1><V2>5</V2></values></sensor></monitor></reading>"
#sample3 = "<configuration><sensor_configuration><sensor><sid>1</sid><location>L1</location></sensor><sensor><sid>2</sid><location>L2</location></sensor></sensor_configuration></configuration>"

#h = HandleXML()
#doc = h.ParseFromString(sample)

#sample4 = "<configuration><monitor_configuration><Monitor_ID>MID</Monitor_ID><OWsource>/dev/ttyS0</OWsource><resolution>600</resolution><destination_address>224.0.0.1</destination_address><destination_port>50000</destination_port><verbose_mode>True</verbose_mode></monitor_configuration></configuration>"
# doc = minidom.parseString(sample)
#doc2 = minidom.parseString(sample2)
#doc3 = minidom.parseString(sample3)

#w = ConfScanner(doc)

#x = ReadingScanner(doc2)
#y = ConfScanner(doc3)

# these should return the same values 

#print w.getAlertDBConfig()
#print w.getAlertSMTPConfig()
#print w.getAlertResolution()
#print w.getAlertVerbose()

#print x.monitor_name
#print x.getMonitorName()
#print x.sensor_data[0]
#print x.getSensorData()
#print x.toReading()
#print y.sensors
#print y.getSensors()